#include "board.h"
#include <rtdevice.h>
#include <rtdef.h>

struct pin_index
{
    GPIO_TypeDef *gpio;
    uint32_t pin;
};

static int gpio_get(rt_base_t pin, struct pin_index *ind)
{
    int g, p;
    int ret = 0;
    GPIO_TypeDef *gpio = 0;

    g = pin >> 8;
    p = pin & 0xff;

    if (p > 15)
        return -1;

    switch (g)
    {
    case 'b':
    {
        gpio = GPIOB;
    }
    break;
    case 'c':
    {
        gpio = GPIOC;
    }
    break;
    default:
    {
        ret = -1;
    }
    break;
    }

    if (ind && ret == 0)
    {
        ind->gpio = gpio;
        ind->pin = 1 << p;
    }

    return ret;
}

static void _pin_mode(struct rt_device *device, rt_base_t pin, rt_base_t mode)
{
    struct pin_index index;
    GPIO_InitTypeDef GPIO_InitStructure;

    if (gpio_get(pin, &index) != 0)
    {
        return;
    }

    GPIO_InitStructure.GPIO_Pin = index.pin;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

    switch (mode)
    {
    case PIN_MODE_OUTPUT:
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
        break;
    }

    GPIO_Init(index.gpio, &GPIO_InitStructure);
}

static void _pin_write(struct rt_device *device, rt_base_t pin, rt_base_t value)
{
    struct pin_index index;

    if (gpio_get(pin, &index) != 0)
    {
        return;
    }

    if (value == PIN_LOW)
    {
        GPIO_ResetBits(index.gpio, index.pin);
    }
    else
    {
        GPIO_SetBits(index.gpio, index.pin);
    }
}

static int _pin_read(struct rt_device *device, rt_base_t pin)
{
    return 0;
}

/* 支持输入 "pa0" "pa.0" "a0" "b.15" "PE0"等样式 */
static rt_base_t _pin_get(struct rt_device *device, const char *name)
{
    rt_base_t pin = -1;
    int len = rt_strlen(name);
    int g = -1, p = -1;

    if (len < 2 || len > 5)
    {
        goto out;
    }

    if (*name == 'p' || *name == 'P')
    {
        name++;
    }

    if (*name >= 'a' && *name <= 'f')
        g = *name;
    else if (*name >= 'A' && *name <= 'F')
        g = *name - 'A' + 'a';

    if (g == -1)
    {
        goto out;
    }

    name++;
    if (*name == '.')
        name++;

    if (*name >= '0' && *name <= '9')
        p = *name - '0';
    if (p == -1)
    {
        goto out;
    }

    name++;
    if (*name >= '0' && *name <= '9')
    {
        p = p * 10;
        p += (*name - '0');
    }

    if (p > 15)
    {
        goto out;
    }

    pin = g << 8 | p;

    if (gpio_get(pin, RT_NULL) != 0)
        pin = -1;

out:
    return pin;
}

static const struct rt_pin_ops _pin_ops = {
    .pin_mode = _pin_mode,
    .pin_write = _pin_write,
    .pin_read = _pin_read,
    .pin_get = _pin_get,
};

int pin_drv_init(void)
{
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);

    rt_device_pin_register("pin", &_pin_ops, 0);

    return 0;
}
INIT_BOARD_EXPORT(pin_drv_init);
